Merge "Test only against protection for deleting"
authorjenkins-bot <jenkins-bot@gerrit.wikimedia.org>
Tue, 19 Aug 2014 17:22:57 +0000 (17:22 +0000)
committerGerrit Code Review <gerrit@wikimedia.org>
Tue, 19 Aug 2014 17:22:57 +0000 (17:22 +0000)
1  2 
includes/Title.php
includes/api/ApiBase.php
languages/i18n/en.json
languages/i18n/qqq.json

diff --combined includes/Title.php
@@@ -158,9 -158,6 +158,9 @@@ class Title 
  
        /** @var TitleValue A corresponding TitleValue object */
        private $mTitleValue = null;
 +
 +      /** @var bool Would deleting this page be a big deletion? */
 +      private $mIsBigDeletion = null;
        // @}
  
        /**
                        $ns = $this->mNamespace == NS_MAIN ?
                                wfMessage( 'nstab-main' )->text() : $this->getNsText();
                        $errors[] = $this->mNamespace == NS_MEDIAWIKI ?
 -                              array( 'protectedinterface' ) : array( 'namespaceprotected', $ns );
 +                              array( 'protectedinterface', $action ) : array( 'namespaceprotected', $ns, $action );
                }
  
                return $errors;
                if ( $action != 'patrol' && !$user->isAllowed( 'editusercssjs' ) ) {
                        if ( preg_match( '/^' . preg_quote( $user->getName(), '/' ) . '\//', $this->mTextform ) ) {
                                if ( $this->isCssSubpage() && !$user->isAllowedAny( 'editmyusercss', 'editusercss' ) ) {
 -                                      $errors[] = array( 'mycustomcssprotected' );
 +                                      $errors[] = array( 'mycustomcssprotected', $action );
                                } elseif ( $this->isJsSubpage() && !$user->isAllowedAny( 'editmyuserjs', 'edituserjs' ) ) {
 -                                      $errors[] = array( 'mycustomjsprotected' );
 +                                      $errors[] = array( 'mycustomjsprotected', $action );
                                }
                        } else {
                                if ( $this->isCssSubpage() && !$user->isAllowed( 'editusercss' ) ) {
 -                                      $errors[] = array( 'customcssprotected' );
 +                                      $errors[] = array( 'customcssprotected', $action );
                                } elseif ( $this->isJsSubpage() && !$user->isAllowed( 'edituserjs' ) ) {
 -                                      $errors[] = array( 'customjsprotected' );
 +                                      $errors[] = array( 'customjsprotected', $action );
                                }
                        }
                }
                                continue;
                        }
                        if ( !$user->isAllowed( $right ) ) {
 -                              $errors[] = array( 'protectedpagetext', $right );
 +                              $errors[] = array( 'protectedpagetext', $right, $action );
                        } elseif ( $this->mCascadeRestriction && !$user->isAllowed( 'protect' ) ) {
 -                              $errors[] = array( 'protectedpagetext', 'protect' );
 +                              $errors[] = array( 'protectedpagetext', 'protect', $action );
                        }
                }
  
                                                foreach ( $cascadingSources as $page ) {
                                                        $pages .= '* [[:' . $page->getPrefixedText() . "]]\n";
                                                }
 -                                              $errors[] = array( 'cascadeprotected', count( $cascadingSources ), $pages );
 +                                              $errors[] = array( 'cascadeprotected', count( $cascadingSources ), $pages, $action );
                                        }
                                }
                        }
                                $errors[] = array( 'immobile-target-page' );
                        }
                } elseif ( $action == 'delete' ) {
-                       if ( count( $this->getUserPermissionsErrorsInternal( 'edit',
-                               $user, $doExpensiveQueries, true ) )
-                       ) {
-                               // If they can't edit, they shouldn't delete.
-                               $errors[] = array( 'delete-cantedit' );
+                       $tempErrors = $this->checkPageRestrictions( 'edit',
+                               $user, array(), $doExpensiveQueries, true );
+                       if( !$tempErrors ) {
+                               $tempErrors = $this->checkCascadingSourcesRestrictions( 'edit',
+                                       $user, $tempErrors, $doExpensiveQueries, true );
+                       }
+                       if ( $tempErrors ) {
+                               // If protection keeps them from editing, they shouldn't be able to delete.
+                               $errors[] = array( 'deleteprotected' );
                        }
                        if ( $doExpensiveQueries && $wgDeleteRevisionsLimit
                                && !$this->userCan( 'bigdelete', $user ) && $this->isBigDeletion()
                                'checkPermissionHooks',
                                'checkReadPermissions',
                        );
 +              # Don't call checkSpecialsAndNSPermissions or checkCSSandJSPermissions
 +              # here as it will lead to duplicate error messages. This is okay to do
 +              # since anywhere that checks for create will also check for edit, and
 +              # those checks are called for edit.
 +              } elseif ( $action == 'create' ) {
 +                      $checks = array(
 +                              'checkQuickPermissions',
 +                              'checkPermissionHooks',
 +                              'checkPageRestrictions',
 +                              'checkCascadingSourcesRestrictions',
 +                              'checkActionPermissions',
 +                              'checkUserBlock'
 +                      );
                } else {
                        $checks = array(
                                'checkQuickPermissions',
                $this->mEstimateRevisions = null;
                $this->mPageLanguage = false;
                $this->mDbPageLanguage = null;
 +              $this->mIsBigDeletion = null;
        }
  
        /**
                        return false;
                }
  
 -              $revCount = $this->estimateRevisionCount();
 -              return $revCount > $wgDeleteRevisionsLimit;
 +              if ( $this->mIsBigDeletion === null ) {
 +                      $dbr = wfGetDB( DB_SLAVE );
 +
 +                      $innerQuery = $dbr->selectSQLText(
 +                              'revision',
 +                              '1',
 +                              array( 'rev_page' => $this->getArticleID() ),
 +                              __METHOD__,
 +                              array( 'LIMIT' => $wgDeleteRevisionsLimit + 1 )
 +                      );
 +
 +                      $revCount = $dbr->query(
 +                              'SELECT COUNT(*) FROM (' . $innerQuery . ') AS innerQuery',
 +                              __METHOD__
 +                      );
 +                      $revCount = $revCount->fetchRow();
 +                      $revCount = $revCount['COUNT(*)'];
 +
 +                      $this->mIsBigDeletion = $revCount > $wgDeleteRevisionsLimit;
 +              }
 +
 +              return $this->mIsBigDeletion;
        }
  
        /**
 -       * Get the  approximate revision count of this page.
 +       * Get the approximate revision count of this page.
         *
         * @return int
         */
diff --combined includes/api/ApiBase.php
@@@ -1349,7 -1349,7 +1349,7 @@@ abstract class ApiBase extends ContextS
                        $msg = wfMessage( $code, $errors[0] );
                }
                if ( isset( ApiBase::$messageMap[$code] ) ) {
 -                      // Translate message to code, for backwards compatability
 +                      // Translate message to code, for backwards compatibility
                        $code = ApiBase::$messageMap[$code]['code'];
                }
  
                        'code' => 'cantedit',
                        'info' => "You can't protect this page because you can't edit it"
                ),
-               'delete-cantedit' => array(
+               'deleteprotected' => array(
                        'code' => 'cantedit',
-                       'info' => "You can't delete this page because you can't edit it"
+                       'info' => "You can't delete this page because it has been protected"
                ),
                'badaccess-group0' => array(
                        'code' => 'permissiondenied',
        /**
         * @see self::getPossibleErrors()
         * @deprecated since 1.24
 -       * @retun array
 +       * @return array
         */
        public function getFinalPossibleErrors() {
                wfDeprecated( __METHOD__, '1.24' );
                }
                print "\n</pre>\n";
        }
 +
 +      /**
 +       * Write logging information for API features to a debug log, for usage
 +       * analysis.
 +       * @param string $feature Feature being used.
 +       */
 +      protected function logFeatureUsage( $feature ) {
 +              $request = $this->getRequest();
 +              $s = $feature .
 +                      ' ' . wfUrlencode( str_replace( ' ', '_', $this->getUser()->getName() ) ) .
 +                      ' ' . $request->getIP() .
 +                      ' "' . $request->getHeader( 'Referer' ) . '"' .
 +                      ' "' . $request->getHeader( 'User-agent' ) . '"';
 +              wfDebugLog( 'api-feature-usage', $s, 'private' );
 +      }
  }
diff --combined languages/i18n/en.json
        "exbeforeblank": "content before blanking was: \"$1\"",
        "delete-confirm": "Delete \"$1\"",
        "delete-legend": "Delete",
 -      "historywarning": "<strong>Warning:</strong> The page you are about to delete has a history with approximately $1 {{PLURAL:$1|revision|revisions}}:",
 +      "historywarning": "<strong>Warning:</strong> The page you are about to delete has a history with $1 {{PLURAL:$1|revision|revisions}}:",
        "confirmdeletetext": "You are about to delete a page along with all of its history.\nPlease confirm that you intend to do this, that you understand the consequences, and that you are doing this in accordance with [[{{MediaWiki:Policy-url}}|the policy]].",
        "actioncomplete": "Action complete",
        "actionfailed": "Action failed",
        "delete-edit-reasonlist": "Edit deletion reasons",
        "delete-toobig": "This page has a large edit history, over $1 {{PLURAL:$1|revision|revisions}}.\nDeletion of such pages has been restricted to prevent accidental disruption of {{SITENAME}}.",
        "delete-warning-toobig": "This page has a large edit history, over $1 {{PLURAL:$1|revision|revisions}}.\nDeleting it may disrupt database operations of {{SITENAME}};\nproceed with caution.",
-       "delete-cantedit": "You cannot delete this page because you do not have permission to edit it.",
+       "deleteprotected": "You cannot delete this page because it has been protected.",
        "deleting-backlinks-warning": "'''Warning:''' [[Special:WhatLinksHere/{{FULLPAGENAME}}|Other pages]] link to or transclude the page you are about to delete.",
        "rollback": "Roll back edits",
        "rollback_short": "Rollback",
diff --combined languages/i18n/qqq.json
        "viewsource-title": "Page title shown when trying to edit a protected page. Parameters:\n* $1 - the name of the page",
        "actionthrottled": "This is the title of an error page. Read it in combination with {{msg-mw|actionthrottledtext}}.",
        "actionthrottledtext": "Used as error message. Read it in combination with {{msg-mw|actionthrottled}}.",
 -      "protectedpagetext": "This message is displayed when trying to edit a page you can't edit because it has been protected.\n\nThe title for this message is {{msg-mw|Protectedpage}}.\n\nParameters:\n* $1 - (Unused) the raw name of the right which is needed to edit the page",
 +      "protectedpagetext": "This message is displayed when trying to edit a page you can't edit because it has been protected.\n\nThe title for this message is {{msg-mw|Protectedpage}}.\n\nParameters:\n* $1 - (Unused) the raw name of the right which is needed to edit the page\n* $2 - (Unused) the action the user attempted to perform",
        "viewsourcetext": "The text shown when displaying the source of a page that the user has no permission to edit",
        "viewyourtext": "Same as {{msg-mw|viewsourcetext}} but when showing the text submitted by the user, this happens e.g. when the user was blocked while he is editing the page",
 -      "protectedinterface": "Message shown if a user without the \"editinterface\" right tries to edit a page in the MediaWiki namespace.\n\nSee also {{msg-mw|editinginterface}}.",
 +      "protectedinterface": "Message shown if a user without the \"editinterface\" right tries to edit a page in the MediaWiki namespace.\n\nSee also {{msg-mw|editinginterface}}. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
        "editinginterface": "A message shown when editing pages in the namespace MediaWiki:.\n\nSee also {{msg-mw|protectedinterface}}.",
 -      "cascadeprotected": "Parameters:\n* $1 - number of cascade-protected pages, used for PLURAL\n* $2 - list of cascade-protected pages",
 -      "namespaceprotected": "Parameters:\n* $1 - namespace name",
 -      "customcssprotected": "Used as error message.",
 -      "customjsprotected": "Used as error message.",
 -      "mycustomcssprotected": "Used as error message.",
 -      "mycustomjsprotected": "Used as error message.",
 +      "cascadeprotected": "Parameters:\n* $1 - number of cascade-protected pages, used for PLURAL\n* $2 - list of cascade-protected pages\n* $3 - (Unused) the action the user attempted to perform",
 +      "namespaceprotected": "Parameters:\n* $1 - namespace name\n* $2 - (Unused) the action the user attempted to perform",
 +      "customcssprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
 +      "customjsprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
 +      "mycustomcssprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
 +      "mycustomjsprotected": "Used as error message. Parameters:\n* $1 - (Unused) the action the user attempted to perform",
        "myprivateinfoprotected": "Used as error message.",
        "mypreferencesprotected": "Used as error message.",
        "ns-specialprotected": "Error message displayed when trying to edit a page in the Special namespace",
        "exbeforeblank": "Automated deletion reason when deleting a page for admins providing that the page was blanked before deletion.\n\nParameters:\n* $1 - content before blanking",
        "delete-confirm": "Used as page title. Parameters:\n* $1 - the page title\n{{Identical|Delete}}",
        "delete-legend": "{{Identical|Delete}}",
 -      "historywarning": "Warning when about to delete a page that has history.\n\nFollowed by a link which points to the history page.\n\nParameters:\n* $1 - the <b>approximate</b> number of revisions that the page has, the message should not claim to give an exact count",
 +      "historywarning": "Warning when about to delete a page that has history.\n\nFollowed by a link which points to the history page.\n\nParameters:\n* $1 - the number of revisions that the page has",
        "confirmdeletetext": "Introduction shown when deleting a page.\n\nRefers to {{msg-mw|Policy-url}}.",
        "actioncomplete": "Used in several situations, for example when a page has been deleted.\n\nSee also:\n* {{msg-mw|Actionfailed|page title}}",
        "actionfailed": "Used as page title when the submit operation failed, in [[Special:RevisionDelete]].\n\nSee also:\n* {{msg-mw|Actioncomplete|page title}}",
        "delete-edit-reasonlist": "Shown beneath the page deletion form on the right side. It is a link to {{msg-mw|Deletereason-dropdown|notext=1}}.\n\nSee also:\n* {{msg-mw|Ipb-edit-dropdown}}\n* {{msg-mw|Protect-edit-reasonlist}}.\n{{Identical|Edit delete reasons}}",
        "delete-toobig": "Parameters:\n* $1 - the upper limit of number of revisions\nSee also:\n* {{msg-mw|Delete-warning-toobig}}",
        "delete-warning-toobig": "Parameters:\n* $1 - the upper limit of number of revisions\nSee also:\n* {{msg-mw|Delete-toobig}}",
-       "delete-cantedit": "Used as error message when deleting the page.",
+       "deleteprotected": "Used as error message when deleting the page.",
        "deleting-backlinks-warning": "A warning shown when a page that is being deleted has at least one link to it or is transcluded in at least one page.",
        "rollback": "{{Identical|Rollback}}",
        "rollback_short": "{{Identical|Rollback}}",
        "protect-summary-cascade": "Used in edit summary when cascade protecting a page. Appears in protection log. See [[Special:Log]] and [[m:Special:Log]].\n\nAlso used in [[Special:ProtectedPages]] when a page is cascade protected. See example: [[m:Special:ProtectedPages]].<br />\nSee also:\n*{{msg-mw|Restriction-level-sysop}}\n*{{msg-mw|Restriction-level-autoconfirmed}}",
        "protect-expiring": "Used as expiry text in page history, and in [[Special:Protectedtitles]], [[Special:Protectedpages]], and extension FlaggedRevs.\n* $1 - a date and time\n* $2 - a date (optional)\n* $3 - a time (optional)\nIf the expiry is indefinite, {{msg-mw|protect-expiry-indefinite}} is used.\n{{Identical|Expires $1 (UTC)}}",
        "protect-expiring-local": "Parameter:\n* $1 - a timestamp like \"22:51, 23 July 2011 (UTC)\" depending on the wiki content language.\n{{Identical|Expire}}",
 -      "protect-expiry-indefinite": "Used as expiry text in page history, and in [[Special:Protectedtitles]], [[Special:Protectedpages]], and extension FlaggedRevs.\n\nIf the expiry is definite, {{msg-mw|protect-expiring}} is used.",
 +      "protect-expiry-indefinite": "Used as expiry text in page history, and in [[Special:Protectedtitles]], [[Special:Protectedpages]], and extension FlaggedRevs.\n\nIf the expiry is definite, {{msg-mw|protect-expiring}} is used.\n{{Identical|Indefinite}}",
        "protect-cascade": "See [[meta:Protect]] for more information.",
        "protect-cantedit": "Used as error message when changing the protection levels of the page.",
        "protect-othertime": "Used on the page protection form as label for the following input field (text)\n{{Identical|Other time}}",
        "block": "{{doc-special|Block}}\n{{Identical|Block user}}",
        "unblock": "{{doc-special|Unblock}}",
        "unblock-summary": "{{doc-specialpagesummary|unblock}}",
 -      "blockip": "Used as the text of a link in the sidebar toolbox. Clicking this link takes you to [[Special:Block]], with a relevant username or IP address (e.g. \"Username\" on [[User talk:Username]], [[Special:Contributions/Username]], etc.) already filled in.\n\n{{Identical|Block user}}",
 +      "blockip": "Used as the text of a link in the sidebar toolbox. Clicking this link takes you to [[Special:Block]], with a relevant username or IP address (e.g. \"Username\" on [[User talk:Username]], [[Special:Contributions/Username]], etc.) already filled in.\n\nParameters:\n* $1 - username, for GENDER support\n{{Identical|Block user}}",
        "blockip-legend": "Legend/Header for the fieldset around the input form of [[Special:Block]].\n\n{{Identical|Block user}}",
        "blockiptext": "Used in the {{msg-mw|Blockip}} form in [[Special:Block]].\n\nRefers to {{msg-mw|Policy-url}}.\n\nThis message may follow the message {{msg-mw|Ipb-otherblocks-header}} and other block messages.\n\nSee also:\n* {{msg-mw|Unblockiptext}}",
        "ipaddressorusername": "{{Identical|IP address or username}}",
        "logentry-pagelang-pagelang": "{{Logentry}}\nAdditional parameters:\n* $4 - old language code, or \"[def]\" (hard-coded)\n* $5 - new language code, or \"[def]\" (hard-coded)",
        "default-skin-not-found": "Message shown when the default skin for this MediaWiki installation can not be found.\n\nParameters:\n* $1: skin identifier for the default skin\n* $2: list of installed skins, composed using {{msg-mw|default-skin-not-found-row-enabled}} and {{msg-mw|default-skin-not-found-row-disabled}}\n* $3: code snippet to use to enable installed skins",
        "default-skin-not-found-no-skins": "Message shown when the default skin for this MediaWiki installation can not be found and the installation has no skins at all.\n\nParameters:\n* $1: name of the default skin",
 -      "default-skin-not-found-row-enabled": "One row of the list of installed skins shown as a part of {{msg-mw|default-skin-not-found}}, for an enabled skin.\n\nParameters:\n* $1: skin identifier\n$2: human-readable skin name",
 +      "default-skin-not-found-row-enabled": "One row of the list of installed skins shown as a part of {{msg-mw|default-skin-not-found}}, for an enabled skin.\n\nParameters:\n* $1: skin identifier\n$2: human-readable skin name",
        "default-skin-not-found-row-disabled": "One row of the list of installed skins shown as a part of {{msg-mw|default-skin-not-found}}, for a disabled skin.\n\nParameters:\n* $1: skin identifier\n$2: human-readable skin name"
  }